﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using ClientDomain.Commnads;
using ClientDomain.Properties;
using Comunication;
using Utiles;

namespace ClientDomain
{
    public class TcpClientPackage : ITcpPacketProcessor
    {
        #region Implementation of ITcpPacketProcessor

        public int AccepReceived(Socket handler, out ITcpPacketProcessor newTcpUtils)
        {
            SocketConnection = handler;
            newTcpUtils = new TcpClientPackage();
            return 0;
        }

        public Socket SocketConnection;

        private byte[] _currentBuffer = new byte[0];

        internal string MD5;
        internal FileStream _fileContent;
        internal String FileName;
        internal int paqueteActual;
        internal int totalPaquetes;
        internal bool accionSubidaArchivo = false;
        internal int cantBytesProcesados = 0;

        internal void RecursivePacket(byte[] buffer, Socket handler)
        {
            if (buffer.Length + _currentBuffer.Length < 10)
            {
                IEnumerable<byte> bytes = _currentBuffer.Concat(buffer);
                _currentBuffer = bytes.ToArray();
            }
            else
            {
                IEnumerable<byte> bytes = _currentBuffer.Concat(buffer);
                byte[] tempArray = bytes.ToArray();

                UTF8Encoding decoder = new UTF8Encoding();
                int dataLength = int.Parse(decoder.GetString(tempArray, 5, 5));
                if (tempArray.Length < (dataLength + 10))
                {
                    _currentBuffer = tempArray;
                }
                else if (tempArray.Length >= (dataLength + 10))
                {
                    byte[] dataInfo = tempArray.Take(dataLength + 10).ToArray();
                    ProcessComand(dataInfo, handler);
                    _currentBuffer = new byte[0];
                    if (tempArray.Length > (dataLength + 10) && tempArray.Skip(dataLength + 10).First().ToString()!="0")
                    {
                        RecursivePacket(tempArray.Skip(dataLength + 10).ToArray(), handler);
                    }
                }
            }

        }

        public void ProcessPacket(byte[] buffer, Socket handler)
        {
            RecursivePacket(buffer,handler);
            //TDOO: borrar temporal
        }

        public void EndConnection(Socket handler)
        {
            handler.Shutdown(SocketShutdown.Both);
            handler.Close();
            IPAddress ipAddress = IPAddress.Parse(Settings.Default.MulticastAddress);
            IPEndPoint remoteep = new IPEndPoint(ipAddress, Convert.ToInt32(Settings.Default.MulticastPort));

            if (accionSubidaArchivo)
            {
                UploadFindServer findServer = new UploadFindServer();
                UDPClient.SendUdpMessage(remoteep, findServer);
            }
            else
            {
                DownloadFindServer downloadFindServer = new DownloadFindServer(FileName, cantBytesProcesados);
                UDPClient.SendUdpMessage(remoteep, downloadFindServer);

            }
        }

        private void ProcessComand(byte[] bufferToProcess, Socket handler)
        {
            UTF8Encoding decoder = new UTF8Encoding();
            string header = decoder.GetString(bufferToProcess, 0, 3);
            string cmd = decoder.GetString(bufferToProcess, 3, 2);
            int dataLength = int.Parse(decoder.GetString(bufferToProcess, 5, 5));
            string data = decoder.GetString(bufferToProcess, 10, dataLength);
            byte[] dataInfo;

            Commands command = (Commands)int.Parse(cmd);

            int ancho = Convert.ToInt32(Settings.Default.MaxBandwith) - 10;
            byte[] dataToSend = new byte[ancho];

            switch (command)
            {
                case Commands.UPLOAD_SERVER_CLIENT_FILE_INFORMATION_RESPONSE:
                    if (header.Equals("RES"))
                    {
                        try
                        {

                            string existsFile = Utilidades.ObtenerInformacion("EXISTS", data, ';');
                            if (existsFile.Equals("N"))
                            {
                                _fileContent = File.OpenRead(ClientAccessComunication.FileName);
                                totalPaquetes = (int)Math.Ceiling(_fileContent.Length / (decimal)ancho);
                                if (totalPaquetes > 1)
                                {
                                    _fileContent.Read(dataToSend, 0, ancho);
                                    ClientServerSendFileIni clientServerSendFileIni = new ClientServerSendFileIni(dataToSend);
                                    TCPClient.SendCommand(handler, clientServerSendFileIni);
                                    accionSubidaArchivo = true;
                                    paqueteActual += 1;
                                }
                                else
                                {
                                    dataToSend = new byte[Convert.ToInt32(_fileContent.Length)];
                                    _fileContent.Read(dataToSend, 0, dataToSend.Length);
                                    ClientServerSendFileUnique clientServerSendFileUnique = new ClientServerSendFileUnique(dataToSend);
                                    TCPClient.SendCommand(handler, clientServerSendFileUnique);
                                    _fileContent.Close();
                                }
                            }
                            else
                            {
                                handler.Shutdown(SocketShutdown.Both);
                                handler.Close();
                                ClientEvents.RaiseErrorUploadingFile("El archivo ya existe");
                            }
                        }
                        catch(Exception ex)
                        {
                            _fileContent.Close();
                            handler.Shutdown(SocketShutdown.Both);
                            handler.Close();
                            ClientEvents.RaiseErrorUploadingFile(ex.ToString());
                        }
                    }
                    break;
                case Commands.UPLOAD_SERVER_CLIENT_NEXT_PACKET:
                    try
                    {
                        //_fileContent.Read(dataToSend, 0, ancho);
                        if (paqueteActual == (totalPaquetes - 1))
                        {
                            dataToSend = new byte[Convert.ToInt32(_fileContent.Length - (paqueteActual * ancho))];
                            _fileContent.Read(dataToSend, 0, dataToSend.Length);
                            ClientServerSendFileEnd clientServerSendFileEnd = new ClientServerSendFileEnd(dataToSend);
                            TCPClient.SendCommand(handler, clientServerSendFileEnd);
                            _fileContent.Close();
                        }
                        else
                        {
                            _fileContent.Read(dataToSend, 0, ancho);
                            ClientServerSendFileContinue clientServerSendFileContinue = new ClientServerSendFileContinue(dataToSend);
                            TCPClient.SendCommand(handler, clientServerSendFileContinue);
                        }
                        paqueteActual += 1;
                    }
                    catch (Exception ex)
                    {
                        _fileContent.Close();
                        handler.Shutdown(SocketShutdown.Both);
                        handler.Close();
                        ClientEvents.RaiseErrorUploadingFile(ex.ToString());
                    }
                    break;
                case Commands.UPLOAD_SERVER_CLIENT_FILE_RECEIVED:
                    if (header.Equals("RES"))
                    {
                        handler.Shutdown(SocketShutdown.Both);
                        handler.Close();
                        ClientEvents.RaiseFileUploadedSuccessfully();
                    }
                    break;
                case Commands.DOWNLOAD_SERVER_CLIENT_FILE_INIT:
                    dataInfo = bufferToProcess.Skip(10).ToArray();
                    if (header.Equals("REQ"))
                    {
                        try
                        {
                            accionSubidaArchivo = false;
                            _fileContent = new FileStream(Settings.Default.FolderPath + FileName, FileMode.Create, FileAccess.Write);
                            _fileContent.Write(dataInfo, 0, dataLength);
                            TCPClient.SendCommand(handler, new DownloadClientServerNextPacket());
                            cantBytesProcesados += dataLength;

                        }
                        catch (Exception exception)
                        {
                            _fileContent.Close();
                            handler.Close();
                            ClientEvents.RaiseErrorDownloadingFile(exception.ToString());
                        }
                    }
                    break;
                case Commands.DOWNLOAD_SERVER_CLIENT_FILE_CONTINUE:
                    dataInfo = bufferToProcess.Skip(10).ToArray();
                    if (header.Equals("REQ"))
                    {
                        try
                        {
                            _fileContent.Write(dataInfo, 0, dataLength);
                            TCPClient.SendCommand(handler, new DownloadClientServerNextPacket());
                            cantBytesProcesados += dataLength;
                        }
                        catch (Exception exception)
                        {
                            _fileContent.Close();
                            handler.Shutdown(SocketShutdown.Both);
                            handler.Close();
                            ClientEvents.RaiseErrorDownloadingFile(exception.ToString());
                        }
                    }
                    break;

                case Commands.DOWNLOAD_SERVER_CLIENT_FILE_END:
                    dataInfo = bufferToProcess.Skip(10).ToArray();
                    if (header.Equals("REQ"))
                    {
                        try
                        {
                            _fileContent.Write(dataInfo, 0, dataLength);
                            _fileContent.Flush(true);
                            _fileContent.Close();
                            if (Utilidades.GetMd5HashFromFile(Settings.Default.FolderPath + FileName) == MD5)
                            {
                                DownloadClientServerFileReceived downloadClientServerFileReceived = new DownloadClientServerFileReceived();
                                TCPClient.SendCommand(handler, downloadClientServerFileReceived);
                                cantBytesProcesados += dataLength;
                                ClientEvents.RaiseFileDownloadedSuccessfully();
                            }
                            else
                            {
                                ClientEvents.RaiseErrorDownloadingFile("Error en la descarga: El MD5 del archivo no coincide.");
                            }
                        }
                        catch (Exception exception)
                        {
                            _fileContent.Close();
                            handler.Shutdown(SocketShutdown.Both);
                            handler.Close();
                            ClientEvents.RaiseErrorDownloadingFile(exception.ToString());
                        }
                    }
                    break;

            }
        }


        #endregion
    }

}
